package com.xqboss.common.core.redis.lock;

import com.xqboss.common.utils.RandomUtil;
import com.xqboss.common.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * @author Trent
 * @date 2023/8/15
 */
@Aspect
@Slf4j
@Component
public class LockAOP {

    @Autowired
    private RedisLockUtils lockUtils;

    @Pointcut("@annotation(RedisLock)")
    public void lockMethod() { // NOSONAR
    }

    @Before("lockMethod()")
    public void beforeAdvice(JoinPoint joinPoint) {
    }

    @After("lockMethod()")
    public void afterAdvice(JoinPoint joinPoint) {
    }

    @Around("lockMethod() && @annotation(lock)")
    public Object aroundAdvice(ProceedingJoinPoint pjp, RedisLock lock) throws Throwable {
        MethodSignature methodSignature = (MethodSignature)pjp.getSignature();
        Method method = methodSignature.getMethod();

        Object[] arguments = pjp.getArgs();
        Parameter[] parameters = method.getParameters();
        StringBuilder sb = new StringBuilder();
        if(parameters != null){
            for(int i = 0 ; i < parameters.length ; i++){
                Parameter parameter = parameters[i];
                if(parameter.getAnnotation(LockValue.class) != null){
                    if(sb.length() > 0){
                        sb.append("-");
                    }
                    sb.append(arguments[i]);
                }else {
                    LockObject lockObject = parameter.getAnnotation(LockObject.class);
                    if(lockObject != null){
                        Object argument = arguments[i];
                        Class<?> clazz = argument.getClass();

                        String[] fieldNames = lockObject.valueFields();
                        if(fieldNames != null && fieldNames.length > 0){
                            // 如果有指定字段列表，则以指定的字段列表为准
                            for (String fieldName : fieldNames) {
                                Field field;
                                try {
                                    field = clazz.getDeclaredField(fieldName);
                                }catch (Exception e){
                                    field = clazz.getSuperclass().getDeclaredField(fieldName);
                                }
                                field.setAccessible(true);
                                Object obj = field.get(argument);
                                if(sb.length() > 0){
                                    sb.append("-");
                                }
                                sb.append(obj);
                            }
                        }else{
                            Field[] fields = clazz.getDeclaredFields();
                            for (Field field : fields) {
                                if(field.getAnnotation(LockValue.class) != null){
                                    field.setAccessible(true);
                                    Object obj = field.get(argument);
                                    if(sb.length() > 0){
                                        sb.append("-");
                                    }
                                    sb.append(obj);
                                }
                            }
                        }
                    }
                }
            }
        }


        String s = RandomUtil.random32();
        String lockKey = lock.lockKey() + ":" + sb;
        if (lock.lockByUser()) {
            lockKey = lockKey + ":" + SecurityUtils.getUserId();
        }
        lockUtils.lock(lockKey, s, lock.timeout(), lock.waitTime());
        try{
            return pjp.proceed();
        }finally {
            lockUtils.unLock(lockKey, s);
        }
    }
}
